package EnrynHsu.affiliate;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *表达式解析引擎.
 */
public class Resolver {
    /**
     * 低精度计算引擎.
     * 基于JVM平台API.
     * 好处：计算快速，消耗资源少：内存以及CPU
     * 坏处：结果简陋
     */
    public static NativeEngine nativeEngine = new NativeEngine();
    /**
     * 高精度计算引擎.
     * 模拟人工
     * 好处：计算精度无损失
     * 坏处：消耗资源大，反应慢
     */
    public static HighEngine highEngine = new HighEngine();
    /**
     * 默认为低精度.
     */
    public static ComputeEngine engine = nativeEngine;

    /**
     * 解析表达式.
     * @param s 要计算的表达式
     * @return 计算结果
     */
    public static String resolve(String s) throws ExpressParseException {
        // 如果表达式最外面没有括号，则加上，方便循环（代码重用）
        if (s == null || s.equals("")) throw new ExpressParseException("没有传入表达式");
        if (Pattern.compile("\\s").matcher(s).find()) throw new ExpressParseException("表达式中不能出现空白符号");
        s = "(" + s + ")";

        // 如果要求解的表达式不是一个数据（结果），那么可以进入循环，进行求解
        // 和下面的匹配解析式必须一致
        while (!s.matches("([+-]?\\d+\\.?(\\d+)?(E-?)?(\\d+)?)|(Infinity)|(NaN)")) {

            // 初始化括号列表：为了解析配对的括号
            ArrayList<Integer> left = new ArrayList<>();
            ArrayList<Integer> right = new ArrayList<>();


            // 获得每一个括号的在原始字符串的位置
            final Pattern pattern_L = Pattern.compile("\\(");
            final Pattern pattern_R = Pattern.compile("\\)");
            final Matcher matcher_L = pattern_L.matcher(s);
            final Matcher matcher_R = pattern_R.matcher(s);
            while (matcher_L.find()) left.add(matcher_L.start());
            while (matcher_R.find()) right.add(matcher_R.start());
            if (left.size() != right.size()) throw new ExpressParseException("括号不配对");

            // 找到配对的左括号和右括号
            int left_i = left.get(left.size() - 1);
            int count = -1;
            boolean error_2 = true;
            for (int i : right) {
                count++;
                if (i > left_i) {
                    error_2 = false;
                    break;
                }
            }
            if (error_2) throw new ExpressParseException("找不到与最后一个左括号配对的右括号");
            int right_i = right.get(count);

            // 获取第一对括号内的子表达式
            String parse = s.substring(left_i + 1, right_i);


            // 获取子表达式马上要进行的运算的操作符，及其位置信息

            // 如果子表达式不是一个数据（结果），则进入循环，求解
            while (!parse.matches("([+-]?\\d+\\.?(\\d+)?(E-?)?(\\d+)?)|(Infinity)|(NaN)")) {

                // 加载操作数容器、操作符容器、和相关等级操作符容器
                ArrayList<String> nums = new ArrayList<>();
                ArrayList<Integer> ops = new ArrayList<>();
                ArrayList<String> op_1 = new ArrayList<>(); // + -
                ArrayList<String> op_2 = new ArrayList<>(); // * / % #
                ArrayList<String> op_3 = new ArrayList<>(); // ^
                ArrayList<String> op_4 = new ArrayList<>(); // @
                // TODO: 2/18/2023 加入相关的等级操作符容器

                // 填充操作符容器和相关等级操作符容器
                final Pattern pattern = Pattern.compile("[*/^@%#&|~](?=(\\d)|-|\\+)|(\\d-)|(\\d\\+)");
                final Matcher matcher = pattern.matcher(parse);
                while (matcher.find()) {
                    if (matcher.group().matches("\\d\\+") || matcher.group().matches("\\d-")) {
                        ops.add(matcher.start() + 1);
                        op_1.add(matcher.group().substring(1) + (matcher.start() + 1));
                    }
                    if (matcher.group().equals("*") || matcher.group().equals("/") || matcher.group().equals("%")
                            || matcher.group().equals("#") || matcher.group().equals("&") || matcher.group().equals("|") || matcher.group().equals("~")) {
                        ops.add(matcher.start());
                        op_2.add(matcher.group() + matcher.start());
                    }
                    if (matcher.group().equals("^")) {
                        ops.add(matcher.start());
                        op_3.add(matcher.group() + matcher.start());
                    }
                    if (matcher.group().equals("@")) {
                        ops.add(matcher.start());
                        op_4.add(matcher.group() + matcher.start());
                    }
                    // TODO: 2/18/2023 模仿模式加入
                }

                // 进行子表达式立马应该进行的运算，并更新
                // TODO: 2/18/2023 模仿模式加入
                if (!op_4.isEmpty()) parse = resolve(parse, nums, ops, op_4);
                else if (!op_3.isEmpty()) parse = resolve(parse, nums, ops, op_3);
                else if (!op_2.isEmpty()) parse = resolve(parse, nums, ops, op_2);
                else if (!op_1.isEmpty()) parse = resolve(parse, nums, ops, op_1);
                else throw new ExpressParseException("优先运算内没有可以进行的操作");
            }

            // 更新源表达式 比如 (1+(1+3*2)) -> (1+7) 或者 ((-1)+(-232323)) -> ((-1)+$0)
            s = s.substring(0, left_i) + parse + s.substring(right_i + 1);

        }
       return s;
    }

    /**
     * 解析子表达式中的运算并更新（每次只运算一次）比如：1+1*3 -> 1+3
     * @param parse 子表达式
     * @param nums 操作数容器：按计划，只能存放两个数
     * @param ops 操作符容器：存放所有的操作符在子表达式中的位置
     * @param op_Info 等级操作符容器：按一定的格式存放的操作符和操作符在子表达式中的位置
     * @return 返回解析（运算一次）后的子表达式
     */
    private static String resolve(String parse, ArrayList<String> nums, ArrayList<Integer> ops, ArrayList<String> op_Info) throws ExpressParseException {
        // 获得等级操作符容器中的第一个操作符在子表达式中的位置
        int item = Integer.parseInt(op_Info.get(0).substring(1));

        // 获取等级操作符容器中的第一个操作符在操作符容器中的索引
        int index = ops.indexOf(item);

        // 如果在子表达式中，等级操作符容器中的第一个操作符的前面和后面都还有操作符
        if (index != 0 && index != ops.size() - 1) {
            int last = ops.get(index - 1);
            int next = ops.get(index + 1);
            // 将操作符两边的操作数放入操作数容器中
            addIntoNum(nums, parse.substring(last + 1, item), parse.substring(item + 1, next));
            // 返回更新后的子表达式
            return parse.substring(0, last + 1) + engine.operate(nums.get(0), nums.get(1), op_Info.get(0).charAt(0)) + parse.substring(next);
        }
        // 如果在子表达式中，等级操作符容器中的第一个操作符的前面没有操作符，而后面有操作符
        else if (index == 0 && index != ops.size() - 1) {
            int next = ops.get(index + 1);
            addIntoNum(nums, parse.substring(0, item), parse.substring(item + 1, next));
            return engine.operate(nums.get(0), nums.get(1), op_Info.get(0).charAt(0)) + parse.substring(next);
        }
        // 如果在子表达式中，等级操作符容器中的第一个操作符的前面有操作符而后面没有
        else if (index != 0 && index == ops.size() - 1) {
            int last = ops.get(index - 1);
            addIntoNum(nums, parse.substring(last + 1, item), parse.substring(item + 1));
            return parse.substring(0, last + 1) + engine.operate(nums.get(0), nums.get(1), op_Info.get(0).charAt(0));
        }
        // 如果在子表达式中，等级操作符容器中的第一个操作符的前面和后面都没有操作符
        else {
            addIntoNum(nums,parse.substring(0, item), parse.substring(item + 1));
            return engine.operate(nums.get(0), nums.get(1), op_Info.get(0).charAt(0));
        }

    }


    /**
     * 按照对应的类型将字符串形式的数据加入操作数容器中
     * @param nums 此方法向此参数所指向的Number ArrayList加入两个元素，或是Double类型，或是Integer类型
     * @param s 从字符串中解析，如果无法解析，则抛出自定义异常
     */
    private static void addIntoNum(ArrayList<String> nums, String ... s) {
        for (String num : s) {
            nums.add(num);
        }
    }




}
